package org.sdfc.query

import java.util.ArrayList
import java.util.Collection
import java.util.HashMap
import java.util.HashSet
import java.util.List
import java.util.Map
import java.util.Set

import com.documentum.fc.client.IDfSession
import com.documentum.fc.common.DfException

import org.sdfc.model.attr.{SingleAttribute => SA}

/** TypedQuery prepared for its execution, targeting data structures
 *  from the Java Collection Library.
 *  <p>
 *  Provides useful extraction methods for the common use cases.
 */
class JclPreparedQuery (
  /** TypedQuery to be executed. */
  val query : TypedQuery,
  /** DFC session. */
  val session : IDfSession
) {
  
  /** Executes the query and collects the values of <code>attr</code> from all rows
   *  into a new Java List.
   */
  @throws(classOf[DfException])
  def extractList[T](attr : SA[T]) : List[T] = {
    val result = new ArrayList[T]
    extractToCollection(attr, result)
    result
  }
  
  /** Executes the query and collects 2-tuples of the values of <code>attr1, attr2</code>
   *  from all rows into a new Java List.
   */
  @throws(classOf[DfException])
  def extractList[T1, T2](attr1 : SA[T1], attr2 : SA[T2]) : List[(T1, T2)] = {
    val result = new ArrayList[(T1, T2)]
    extractToCollection(attr1, attr2, result)
    result
  }
  
  /** Executes the query and collects 3-tuples of the values of <code>attr1, attr2, attr3</code>
   *  from all rows into a new Java List.
   */
  @throws(classOf[DfException])
  def extractList[T1, T2, T3](attr1 : SA[T1], attr2 : SA[T2], attr3 : SA[T3]) : List[(T1, T2, T3)] = {
    val result = new ArrayList[(T1, T2, T3)]
    extractToCollection(attr1, attr2, attr3, result)
    result
  }
  
  /** Executes the query and collects 4-tuples of the values of <code>attr1, attr2, attr3, attr4</code>
   *  from all rows into a new Java List.
   */
  @throws(classOf[DfException])
  def extractList[T1, T2, T3, T4](attr1 : SA[T1], attr2 : SA[T2], attr3 : SA[T3], attr4 : SA[T4]) : List[(T1, T2, T3, T4)] = {
    val result = new ArrayList[(T1, T2, T3, T4)]
    extractToCollection(attr1, attr2, attr3, attr4, result)
    result
  }
  
  /** Executes the query and collects 5-tuples of the values of <code>attr1, attr2, attr3, attr4, attr5</code>
   *  from all rows into a new Java List.
   */
  @throws(classOf[DfException])
  def extractList[T1, T2, T3, T4, T5](attr1 : SA[T1], attr2 : SA[T2], attr3 : SA[T3], attr4 : SA[T4], attr5 : SA[T5]) : List[(T1, T2, T3, T4, T5)] = {
    val result = new ArrayList[(T1, T2, T3, T4, T5)]
    extractToCollection(attr1, attr2, attr3, attr4, attr5, result)
    result
  }
  
  
  /** Executes the query and collects the values of <code>attr</code> from all rows
   *  into a new Java Set.
   */
  @throws(classOf[DfException])
  def extractSet[T](attr : SA[T]) : Set[T] = {
    val result = new HashSet[T]
    extractToCollection(attr, result)
    result
  }
  
  /** Executes the query and collects 2-tuples of the values of <code>attr1, attr2</code>
   *  from all rows into a new Java Set.
   */
  @throws(classOf[DfException])
  def extractSet[T1, T2](attr1 : SA[T1], attr2 : SA[T2]) : Set[(T1, T2)] = {
    val result = new HashSet[(T1, T2)]
    extractToCollection(attr1, attr2, result)
    result
  }
  
  /** Executes the query and collects 3-tuples of the values of <code>attr1, attr2, attr3</code>
   *  from all rows into a new Java Set.
   */
  @throws(classOf[DfException])
  def extractSet[T1, T2, T3](attr1 : SA[T1], attr2 : SA[T2], attr3 : SA[T3]) : Set[(T1, T2, T3)] = {
    val result = new HashSet[(T1, T2, T3)]
    extractToCollection(attr1, attr2, attr3, result)
    result
  }
  
  /** Executes the query and collects 4-tuples of the values of <code>attr1, attr2, attr3, attr4</code>
   *  from all rows into a new Java Set.
   */
  @throws(classOf[DfException])
  def extractSet[T1, T2, T3, T4](attr1 : SA[T1], attr2 : SA[T2], attr3 : SA[T3], attr4 : SA[T4]) : Set[(T1, T2, T3, T4)] = {
    val result = new HashSet[(T1, T2, T3, T4)]
    extractToCollection(attr1, attr2, attr3, attr4, result)
    result
  }
  
  /** Executes the query and collects 5-tuples of the values of <code>attr1, attr2, attr3, attr4, attr5</code>
   *  from all rows into a new Java Set.
   */
  @throws(classOf[DfException])
  def extractSet[T1, T2, T3, T4, T5](attr1 : SA[T1], attr2 : SA[T2], attr3 : SA[T3], attr4 : SA[T4], attr5 : SA[T5]) : Set[(T1, T2, T3, T4, T5)] = {
    val result = new HashSet[(T1, T2, T3, T4, T5)]
    extractToCollection(attr1, attr2, attr3, attr4, attr5, result)
    result
  }
  
  
  /** Executes the query and collects the values of <code>attr</code> from all rows
   *  into a given Java Collection.
   */
  @throws(classOf[DfException])
  def extractToCollection[T](attr : SA[T], acc : Collection[T]) : Unit = {
    val gf = attr.genericForm
    for ( row <- query.rows(session) )
      acc.add(gf.get(row))
  }
  
  /** Executes the query and collects 2-tuples of the values of <code>attr1, attr2</code>
   *  from all rows into a given Java Collection.
   */
  @throws(classOf[DfException])
  def extractToCollection[T1, T2](attr1 : SA[T1], attr2 : SA[T2], acc : Collection[(T1, T2)]) : Unit = {
    val gf1 = attr1.genericForm
    val gf2 = attr2.genericForm
    for ( row <- query.rows(session) )
      acc.add( (gf1.get(row), gf2.get(row)) )
  }
  
  /** Executes the query and collects 3-tuples of the values of <code>attr1, attr2, attr3</code>
   *  from all rows into a given Java Collection.
   */
  @throws(classOf[DfException])
  def extractToCollection[T1, T2, T3](attr1 : SA[T1], attr2 : SA[T2], attr3 : SA[T3], acc : Collection[(T1, T2, T3)]) : Unit = {
    val gf1 = attr1.genericForm
    val gf2 = attr2.genericForm
    val gf3 = attr3.genericForm
    for ( row <- query.rows(session) )
      acc.add( (gf1.get(row), gf2.get(row), gf3.get(row)) )
  }
  
  /** Executes the query and collects 4-tuples of the values of <code>attr1, attr2, attr3, attr4</code>
   *  from all rows into a given Java Collection.
   */
  @throws(classOf[DfException])
  def extractToCollection[T1, T2, T3, T4](attr1 : SA[T1], attr2 : SA[T2], attr3 : SA[T3], attr4 : SA[T4], acc : Collection[(T1, T2, T3, T4)]) : Unit = {
    val gf1 = attr1.genericForm
    val gf2 = attr2.genericForm
    val gf3 = attr3.genericForm
    val gf4 = attr4.genericForm
    for ( row <- query.rows(session) )
      acc.add( (gf1.get(row), gf2.get(row), gf3.get(row), gf4.get(row)) )
  }
  
  /** Executes the query and collects 5-tuples of the values of <code>attr1, attr2, attr3, attr4, attr5</code>
   *  from all rows into a given Java Collection.
   */
  @throws(classOf[DfException])
  def extractToCollection[T1, T2, T3, T4, T5](attr1 : SA[T1], attr2 : SA[T2], attr3 : SA[T3], attr4 : SA[T4], attr5 : SA[T5], acc : Collection[(T1, T2, T3, T4, T5)]) : Unit = {
    val gf1 = attr1.genericForm
    val gf2 = attr2.genericForm
    val gf3 = attr3.genericForm
    val gf4 = attr4.genericForm
    val gf5 = attr5.genericForm
    for ( row <- query.rows(session) )
      acc.add( (gf1.get(row), gf2.get(row), gf3.get(row), gf4.get(row), gf5.get(row)) )
  }
  
  
  /** Executes the query and collects mappings from values of <code>kAttr</code> to values of <code>vAttr</code>
   *  from all rows into a new Java Map.
   */
  @throws(classOf[DfException])
  def extractMap[K, V](kAttr : SA[K], vAttr : SA[V]) : Map[K, V] = {
	val result = new HashMap[K, V]
	extractToMap(kAttr, vAttr, result)
	result
  }
  
  /** Executes the query and collects mappings from values of <code>kAttr</code> to values of <code>vAttr</code>
   *  from all rows into a new Java Map.
   */
  @throws(classOf[DfException])
  def extractToMap[K, V](kAttr : SA[K], vAttr : SA[V], acc : Map[K, V]) : Unit = {
    val kGf = kAttr.genericForm
    val vGf = vAttr.genericForm
    for ( row <- query.rows(session) )
      acc.put(kGf.get(row), vGf.get(row))
  }
}